package top.zhangjianyong.tools.indicator.strategy.impl;

import org.springframework.stereotype.Component;
import top.zhangjianyong.tools.entity.ETFData;
import top.zhangjianyong.tools.indicator.context.IndicatorContext;
import top.zhangjianyong.tools.indicator.model.TechnicalIndicatorResult;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.List;

/**
 * CMO（Chande动量振荡器）指标计算器
 * 窗口14
 *
 * @author zhangjianyong
 * @date 2024/01/01
 */
@Component
public class CMOIndicator extends AbstractIndicator {

    private static final String INDICATOR_NAME = "CMO";
    private static final int PERIOD = 14;

    @Override
    public String getName() {
        return INDICATOR_NAME;
    }

    @Override
    public int getMinDataSize() {
        return PERIOD + 1; // 需要至少15个数据点
    }

    @Override
    protected TechnicalIndicatorResult doCalculate(IndicatorContext context) {
        List<ETFData> dataList = context.getDataList();

        // 计算价格上涨和下跌的总和
        BigDecimal sumUp = BigDecimal.ZERO;
        BigDecimal sumDown = BigDecimal.ZERO;

        // 从最后PERIOD个数据点开始计算
        int startIndex = Math.max(1, dataList.size() - PERIOD);
        for (int i = startIndex; i < dataList.size(); i++) {
            ETFData current = dataList.get(i);
            ETFData previous = dataList.get(i - 1);

            BigDecimal priceChange = current.getValue().subtract(previous.getValue());

            if (priceChange.compareTo(BigDecimal.ZERO) > 0) {
                // 价格上涨
                sumUp = sumUp.add(priceChange);
            } else if (priceChange.compareTo(BigDecimal.ZERO) < 0) {
                // 价格下跌
                sumDown = sumDown.add(priceChange.abs());
            }
        }

        // 计算CMO
        // CMO = ((sumUp - sumDown) / (sumUp + sumDown)) * 100
        BigDecimal cmo = BigDecimal.ZERO;
        BigDecimal sumTotal = sumUp.add(sumDown);

        if (sumTotal.compareTo(BigDecimal.ZERO) != 0) {
            cmo = sumUp.subtract(sumDown)
                .divide(sumTotal, 4, RoundingMode.HALF_UP)
                .multiply(new BigDecimal(100));
        }

        // 限制在-100到100范围内
        if (cmo.compareTo(new BigDecimal(-100)) < 0) {
            cmo = new BigDecimal(-100);
        } else if (cmo.compareTo(new BigDecimal(100)) > 0) {
            cmo = new BigDecimal(100);
        }

        TechnicalIndicatorResult result = TechnicalIndicatorResult.success(getName());
        result.addValue("cmo", cmo.setScale(2, RoundingMode.HALF_UP));
        result.addValue("cmo_14", cmo.setScale(2, RoundingMode.HALF_UP));

        return result;
    }
}
